package trading_strategy.strategies;

import org.apache.log4j.Logger;
import trading_strategy.Strategy;
import trading_strategy.events.AbstractMarketEvent;
import trading_strategy.events.MarketEvent;
import trading_strategy.events.PublicTradeUpdate;
import trading_strategy.events.filtered.AMMFilter;
import trading_strategy.instruments.ListedInstrument;
import trading_strategy.orders.SingleOrderAutomaton;
import trading_strategy.positions.Trade;

/**
 * Once we have entered a position, this class is responsible to exit the strategy, either through  stop-loss event, or after
 * a defined target has been reached
 *
 * Created by IntelliJ IDEA.
 * User: gderoujoux
 * Date: 24 mai 2010
 * Time: 18:17:46
 * To change this template use File | Settings | File Templates.
 */
public class TriggerPositionExiter extends PositionTaker {
    // Trade that triggered entry into position
    Trade entryTrade;

    // Stop loss value in underlying points
    double stopLoss;

    // Initial target profit in underlying points
    double targetProfit;

    // In order not to cut the position too fast, we readjust our target if it goes in the right direction
    double currentTargetPrice;

    double bestPriceSeen;

    long maxTsBeforeExit;

    // If the spot pulls back of more than this value, take profit
    double pullBackThreshold;
    public static final Logger LOGGER = Logger.getLogger(TriggerPositionExiter.class);
    private AMMFilter.AMMEvent lastAmm        ;
    private long maxTsForConfirm;
    private boolean crossedMovingAverage = false;
    double confirmProfitRatio = 1d/3d;
    private int mmaCrossingThreshold = 2;

    public TriggerPositionExiter(ListedInstrument instrument, PositionEntererListener strategy, SingleOrderAutomaton automaton, Trade entryTrade, double stopLoss, double targetProfit, double pullBackThreshold, long maxTsBeforeExit, long maxTsForConfirm, AMMFilter amm28) {
        super(instrument, entryTrade.getSignedQty() > 0 ? Strategy.Direction.SELL : Strategy.Direction.BUY, strategy, automaton);
        this.entryTrade = entryTrade;
        boolean selling = entryTrade.getSignedQty() > 0;
        this.stopLoss = stopLoss;
        this.targetProfit = targetProfit;
        this.pullBackThreshold = pullBackThreshold;
        this.currentTargetPrice = entryTrade.getPrice() + (selling ? 1 : -1) * targetProfit;
        this.bestPriceSeen = entryTrade.getPrice();
        this.maxTsBeforeExit = maxTsBeforeExit;
        this.maxTsForConfirm = maxTsForConfirm;
        if (amm28 != null) {
            amm28.addListener(this);
        }
    }

    @Override
    public void onMarketEvent(AbstractMarketEvent event) {
        if (event instanceof MarketEvent) {
            if (((MarketEvent)event).getState() == MarketEvent.MarketState.CLOSED) {
                hit(event.getTs());
            }
        } else {
            if (event instanceof AMMFilter.AMMEvent) {
                AMMFilter.AMMEvent ammEvent = (AMMFilter.AMMEvent) event;
                lastAmm = ammEvent;
            }
            super.onMarketEvent(event);    //To change body of overridden methods use File | Settings | File Templates.
        }
    }

    @Override
    protected void handleTrade(PublicTradeUpdate ptu) {
        if (cancelled) {
            return;
        }
        if (direction == Strategy.Direction.BUY) {
            if (ptu.getPrice() < bestPriceSeen) {
                bestPriceSeen = ptu.getPrice();
            }

            double bestProfitSeen = -(bestPriceSeen - entryTrade.getPrice());
            if (bestPriceSeen <= currentTargetPrice && ptu.getPrice() >= bestPriceSeen + pullBackThreshold) {
                // Pull back, hit the market
                hit(ptu.getTs(), "Pull back threshold");
            } else if (ptu.getPrice() >= entryTrade.getPrice() + stopLoss) {
                // Stop-loss reached
                hit(ptu.getTs(), "Stop loss");
            } else if (ptu.getTs() > maxTsForConfirm && bestProfitSeen < targetProfit * confirmProfitRatio) {
                // Stop-loss reached
                hit(ptu.getTs(), "Did not confirm");
            } else if (ptu.getTs() > maxTsBeforeExit && bestPriceSeen > entryTrade.getPrice() - targetProfit) {
                // We did not reach our inital target before deadline, cut
                hit(ptu.getTs(), "Timed out");
            } else if (lastAmm != null && ((ptu.getPrice()- lastAmm.getValue()) < mmaCrossingThreshold)) {
                crossedMovingAverage = true;
            } else if (crossedMovingAverage && ((ptu.getPrice() - lastAmm.getValue()) > -mmaCrossingThreshold || entryTrade.getPrice() < lastAmm.getValue())) {
                // We crossed the moving average
                hit(ptu.getTs(), "Crossed moving Average");
            }
        } else if (direction == Strategy.Direction.SELL) {
            if (ptu.getPrice() > bestPriceSeen) {
                bestPriceSeen = ptu.getPrice();
            }

            double bestProfitSeen = bestPriceSeen - entryTrade.getPrice();
            if (bestPriceSeen >= currentTargetPrice && ptu.getPrice() <= bestPriceSeen - pullBackThreshold) {
                // Pull back, hit the market
                hit(ptu.getTs(), "Pull back threshold");
            } else if (ptu.getTs() > maxTsForConfirm && bestProfitSeen < targetProfit * confirmProfitRatio) {
                // Stop-loss reached
                hit(ptu.getTs(), "Did not confirm");
            } else if (ptu.getPrice() <= entryTrade.getPrice() - stopLoss) {
                // Stop-loss reached
                hit(ptu.getTs(), "Stop loss");
            } else if (ptu.getTs() > maxTsBeforeExit && bestPriceSeen < entryTrade.getPrice() + targetProfit) {
                // We did not reach our inital target before deadline, cut
                hit(ptu.getTs(), "Timed out");
            } else if (lastAmm != null && ((ptu.getPrice() - lastAmm.getValue()) > -mmaCrossingThreshold)) {
                crossedMovingAverage = true;
            } else if (crossedMovingAverage && ((ptu.getPrice() - lastAmm.getValue()) < mmaCrossingThreshold || entryTrade.getPrice() > lastAmm.getValue())) {
                // We crossed the moving average
                hit(ptu.getTs(), "Crossed moving Average " + lastAmm.getValue() + "@" + ptu.getDate());
            }
        }
    }

    protected void hit(long ts, String cause) {
        LOGGER.info("Exiting because: " + cause);
        super.hit(ts);    //To change body of overridden methods use File | Settings | File Templates.
    }
}
